package cn.witsky.rtp2ws.udp.impl.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Iterator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.witsky.rtp2ws.Server;
import cn.witsky.rtp2ws.cache.UdpServerManager;
import cn.witsky.rtp2ws.domain.CallInfo;
import cn.witsky.rtp2ws.udp.UdpServer;
import cn.witsky.rtp2ws.util.RtpUtil;

public class NioUdpServer implements UdpServer {
	private static int headLen = 12;
	private static Logger logger = LoggerFactory.getLogger(NioUdpServer.class);
	DatagramChannel channel;
	Selector selector;
	private Integer udpPort;
	private ReciveRunable task;

	public NioUdpServer(Integer udpPort) {
		this.udpPort = udpPort;
	}

	@Override
	public Integer getUdpPort() {
		return udpPort;
	}

	@Override
	public boolean start() {
		if (task != null) {
			logger.warn("线程已经启动了。。。。");
			return false;
		} else {
			return this.bindPort();
		}
	}

	private boolean bindPort() {
		try {
			logger.info("开始启动接监听udp:{}端口的数据了", this.udpPort);
			// 打开一个UDP Channel
			channel = DatagramChannel.open();
			// 设定为非阻塞通道
			channel.configureBlocking(false);
			// 绑定端口
			channel.socket().bind(new InetSocketAddress(this.getUdpPort()));
			// 打开一个选择器
			selector = Selector.open();
			channel.register(selector, SelectionKey.OP_READ);
			task = new ReciveRunable(selector, this.udpPort);
			Thread t = new Thread(task);
			t.setName("rtpReadThread-" + this.getUdpPort());
			t.start();
			return true;
		} catch (Exception e) {
			logger.error("绑定端口出现异常", e);
			return false;
		}
	}

	@Override
	public void stop() {
		if (!task.isStop) {
			task.setStop(true);
			this.destory();
		}
	}

	class ReciveRunable implements Runnable {
		private volatile boolean isStop = false;

		public boolean isStop() {
			return isStop;
		}

		public void setStop(boolean isStop) {
			this.isStop = isStop;
		}

		Selector selector;
		int udpPort;

		public ReciveRunable(Selector selector, Integer udpPort) {
			this.selector = selector;
			this.udpPort = udpPort;
		}

		@Override
		public void run() {
			while (true) {
				if (this.isStop) {
					break;
				}
				ByteBuffer byteBuffer = ByteBuffer.allocate(172);
				try {
					// 进行选择
					int n = selector.select();
					if (n > 0) {
						// 获取以选择的键的集合
						Iterator iterator = selector.selectedKeys().iterator();
						while (iterator.hasNext()) {
							SelectionKey key = (SelectionKey) iterator.next();
							// 必须手动删除
							iterator.remove();
							if (key.isReadable()) {
								DatagramChannel datagramChannel = (DatagramChannel) key.channel();
								read(datagramChannel);
								// 删除缓冲区中的数据
								byteBuffer.clear();
							}
						}
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}

	}

	void read(DatagramChannel datagramChannel) {
		int size = headLen + Server.getPayLoadLen();
		ByteBuffer byteBuffer = ByteBuffer.allocate(size);
		try {
			InetSocketAddress address = (InetSocketAddress) datagramChannel.receive(byteBuffer);
			int capacity = byteBuffer.position();
			String srcHost = address.getHostString();
			int srcPort = address.getPort();
			String rtpSource = srcHost + ":" + srcPort;
			if (capacity - headLen < Server.getPayLoadLen()) {
				logger.debug("rtpSource:{}  writeLen:{} rtpPayload:{}该数据包不是有效的数据包，忽略。。。。。。。。", rtpSource, capacity,
						Server.getPayLoadLen());
				return;
			}
			CallInfo call = UdpServerManager.getCall(rtpSource, this.getUdpPort());
			if (null != call) {
				call.bindSrcPort(srcHost, srcPort, this.getUdpPort());
				byte[] reciveByte = byteBuffer.array();
				int seq = RtpUtil.getSeq(reciveByte);
				byte[] payload = new byte[capacity - headLen];
				System.arraycopy(reciveByte, headLen, payload, 0, payload.length);
//				call.offerMessage(rtpSource, this.getUdpPort(), seq, payload);
			} else {
				logger.debug("rtpSource:{}-->dstPort:{} 的会话是空，不处理该数据", rtpSource, this.getUdpPort());
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private void destory() {
		logger.info("服务器正在停止监听{}端口......", this.udpPort);
		try {
			channel.close();
			logger.info("服务器停止监听{}端口", this.udpPort);
		} catch (Exception e) {
			logger.error("服务器停止监听" + this.udpPort + "端口出现异常", e);
		}
	}
}
